package cn.da.shuai.cool.finance.service.impl;

import cn.da.shuai.cool.finance.model.CoolAccountsPayable;
import cn.da.shuai.cool.finance.mapper.CoolAccountsPayableMapper;
import cn.da.shuai.cool.finance.service.ICoolAccountsPayableService;
import cn.da.shuai.cool.mybatis.service.impl.CoolServiceImpl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class CoolAccountsPayableServiceImpl extends CoolServiceImpl<CoolAccountsPayableMapper, CoolAccountsPayable> implements ICoolAccountsPayableService {

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean recordPayable(CoolAccountsPayable payable) {
        payable.setRemainingAmount(payable.getAmount());
        payable.setPaidAmount(BigDecimal.ZERO);
        payable.setStatus("PENDING");
        return save(payable);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean recordPayment(String invoiceNo, BigDecimal amount, String paymentReference) {
        CoolAccountsPayable payable = getOne(new LambdaQueryWrapper<CoolAccountsPayable>()
                .eq(CoolAccountsPayable::getInvoiceNo, invoiceNo));

        if (payable == null) {
            throw new RuntimeException("Invoice not found");
        }

        BigDecimal newPaidAmount = payable.getPaidAmount().add(amount);
        BigDecimal newRemainingAmount = payable.getAmount().subtract(newPaidAmount);

        payable.setPaidAmount(newPaidAmount);
        payable.setRemainingAmount(newRemainingAmount);

        if (newRemainingAmount.compareTo(BigDecimal.ZERO) == 0) {
            payable.setStatus("PAID");
        } else {
            payable.setStatus("PARTIALLY_PAID");
        }

        // TODO: Record the payment in a separate payments table

        return updateById(payable);
    }

    @Override
    public List<CoolAccountsPayable> getOverduePayables() {
        LocalDate today = LocalDate.now();
        return list(new LambdaQueryWrapper<CoolAccountsPayable>()
                .lt(CoolAccountsPayable::getDueDate, today)
                .ne(CoolAccountsPayable::getStatus, "PAID"));
    }

    @Override
    public Map<String, Object> getAgingAnalysis() {
        List<CoolAccountsPayable> payables = list(new LambdaQueryWrapper<CoolAccountsPayable>()
                .ne(CoolAccountsPayable::getStatus, "PAID"));

        LocalDate today = LocalDate.now();
        Map<String, BigDecimal> agingBuckets = new HashMap<>();
        agingBuckets.put("0-30", BigDecimal.ZERO);
        agingBuckets.put("31-60", BigDecimal.ZERO);
        agingBuckets.put("61-90", BigDecimal.ZERO);
        agingBuckets.put("90+", BigDecimal.ZERO);

        for (CoolAccountsPayable payable : payables) {
            long daysOverdue = payable.getDueDate().until(today).getDays();
            BigDecimal remainingAmount = payable.getRemainingAmount();

            if (daysOverdue <= 30) {
                agingBuckets.put("0-30", agingBuckets.get("0-30").add(remainingAmount));
            } else if (daysOverdue <= 60) {
                agingBuckets.put("31-60", agingBuckets.get("31-60").add(remainingAmount));
            } else if (daysOverdue <= 90) {
                agingBuckets.put("61-90", agingBuckets.get("61-90").add(remainingAmount));
            } else {
                agingBuckets.put("90+", agingBuckets.get("90+").add(remainingAmount));
            }
        }

        return new HashMap<>(agingBuckets);
    }

    @Override
    public BigDecimal getVendorBalance(Long vendorId) {
        List<CoolAccountsPayable> vendorPayables = list(new LambdaQueryWrapper<CoolAccountsPayable>()
                .eq(CoolAccountsPayable::getVendorId, vendorId)
                .ne(CoolAccountsPayable::getStatus, "PAID"));

        return vendorPayables.stream()
                .map(CoolAccountsPayable::getRemainingAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    @Override
    public List<Map<String, Object>> getPayablesTrend(LocalDate startDate, LocalDate endDate) {
        List<CoolAccountsPayable> payables = list(new LambdaQueryWrapper<CoolAccountsPayable>()
                .between(CoolAccountsPayable::getInvoiceDate, startDate, endDate));

        Map<LocalDate, BigDecimal> dailyTotals = payables.stream()
                .collect(Collectors.groupingBy(
                        CoolAccountsPayable::getInvoiceDate,
                        Collectors.reducing(BigDecimal.ZERO, CoolAccountsPayable::getAmount, BigDecimal::add)
                ));

        return dailyTotals.entrySet().stream()
                .map(entry -> {
                    Map<String, Object> dailyTotal = new HashMap<>();
                    dailyTotal.put("date", entry.getKey());
                    dailyTotal.put("amount", entry.getValue());
                    return dailyTotal;
                })
                .collect(Collectors.toList());
    }

    @Override
    public Map<LocalDate, BigDecimal> getCashRequirementsForecast() {
        List<CoolAccountsPayable> payables = list(new LambdaQueryWrapper<CoolAccountsPayable>()
                .ne(CoolAccountsPayable::getStatus, "PAID"));

        Map<LocalDate, BigDecimal> dailyRequirements = new TreeMap<>();

        for (CoolAccountsPayable payable : payables) {
            LocalDate dueDate = payable.getDueDate();
            BigDecimal amount = payable.getRemainingAmount();

            dailyRequirements.merge(dueDate, amount, BigDecimal::add);
        }

        return new HashMap<>(dailyRequirements);
    }

    @Override
    public List<CoolAccountsPayable> getPaymentPriorities() {
        LocalDate today = LocalDate.now();
        List<CoolAccountsPayable> unpaidPayables = list(new LambdaQueryWrapper<CoolAccountsPayable>()
                .ne(CoolAccountsPayable::getStatus, "PAID"));

        return unpaidPayables.stream()
                .sorted(Comparator
                        .comparing(CoolAccountsPayable::getDueDate)
                        .thenComparing(CoolAccountsPayable::getAmount).reversed())
                .collect(Collectors.toList());
    }
}